package com.dns.server.primary;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

import com.dns.message.DNS_Message;
import com.dns.util.Constants;

public class RequestHandler implements Runnable{

	private DNS_Message dnsMsg=null;
	private DatagramSocket socket=null;
	private Address_Lookup addressLookUp=null;

	public RequestHandler(){
		
	}
	
	public RequestHandler(DNS_Message dnsMsg,DatagramSocket socket,Address_Lookup adrsLkup){
		this.dnsMsg=dnsMsg;
		this.socket=socket;
		this.addressLookUp=adrsLkup;
	}
	
	@Override
	public void run() {
		//System.out.println(this.getClass().getName()+ ": Info: Starting a new Thread to lookup address");
		if(dnsMsg!=null){
			if(dnsMsg.getDomainName()==null && dnsMsg.getIp_address()==null){
				System.out.println(this.getClass().getName()+": Error: Invalid Domain Name or IP address in the message ");
				return;
			}
			if(dnsMsg.getSource()==null && dnsMsg.getMsgType()>3){
				System.out.println(this.getClass().getName()+": Error: Invalid Message source or Messag type ");
				return;
			}
			if(dnsMsg.getSource().equalsIgnoreCase(Constants.CLIENT)){
				//if the message type is inquiry do the look up
				if(dnsMsg.getMsgType()==Constants.MSG_INQUIRY){
					if(!addressLookUp.lookUp(dnsMsg)){
					//set the source as primary
					dnsMsg.setSource(Constants.PRIMARY);
					System.out.println(this.getClass().getName()+ ": Info: Domain name or IP address doesn't exist in the primary cahce, Sending the Request to Secondary server ");
					// domain name not found in the primary server 
					//send the request to secondary server
					forwardToSecondary();
					}
					else{
						dnsMsg.setSource(Constants.PRIMARY);
						dnsMsg.setMsgType(Constants.MSG_RESPONSE);
						dnsMsg.setResult(Constants.SUCCESS);
						sendToClient();
					}
				}
				else if(dnsMsg.getMsgType()==Constants.MSG_UPDATE){
					forwardToSecondary();
				}
			}
			else if(dnsMsg.getSource().equalsIgnoreCase(Constants.SECONDARY)){
				if(dnsMsg.getMsgType()==Constants.MSG_RESPONSE){
					//add the domain and IP address to the cache
					if(dnsMsg.getResult()==Constants.SUCCESS)
						addressLookUp.updateAddress(dnsMsg);
					//respond back to client
					dnsMsg.setSource(Constants.PRIMARY);
					sendToClient();
				}
				else if(dnsMsg.getMsgType()==Constants.MSG_UPDATE){
					//add the domain name & IP to the cache
					addressLookUp.updateAddress(dnsMsg);
				}
				else if(dnsMsg.getMsgType()==Constants.MSG_REGISTER){
					// register the secondary server name and port
					if(dnsMsg.getDomainName()==null || dnsMsg.getIp_address()==null || dnsMsg.getClientPort()==0){
						System.out.println(this.getClass().getName()+": Error: Invalid Domain Name or IP address in the DNS Register message ");
						return ;
					}
					ServerRegistry.regiserSecondarServer(dnsMsg);
				}
			}
			
		}
	}
	
/*	
	public void recvMessage(){
		
		ByteArrayInputStream byteIn =null;
		ObjectInputStream objectIn = null;
		DatagramPacket recvdPacket=pktFlow.getRecvdPacket();
		byteIn = new ByteArrayInputStream(recvdPacket.getData());
		
		try{
			//String dnsMsg = new String(recvPacket.getData(),0, recvPacket.getLength());
			objectIn = new ObjectInputStream(byteIn);
			DNS_Message dnsMsg = (DNS_Message) objectIn.readObject();
			//System.out.println("Client IP "+recvdPacket.getAddress().getHostAddress()+" Port "+recvdPacket.getPort());
			pktFlow.setLookup_dnsMsg(dnsMsg);
			System.out.println(this.getClass().getName()+ ": Info: Message Received : \n Type : "+dnsMsg.getMsgType()+" \n Source : "+dnsMsg.getSource()+" \n Domain Nmae : "+dnsMsg.getDomainName()+"\n IP : "+dnsMsg.getIp_address());
		}
		catch (IOException e) {
			System.out.println(this.getClass().getName()+": Error : Exception while de-serializing the DNS_Message object");
			e.printStackTrace();
		}
		catch (ClassNotFoundException e) {
			System.out.println(this.getClass().getName()+": Error : Exception while creating the deserialized DNS_Message object");
			e.printStackTrace();
		}
		finally{
			byteIn=null;
			objectIn=null;
		}
	}
*/	
	public void forwardToSecondary(){
		//DNS_Message dnsMsg =pktFlow.getLookup_dnsMsg();
		SecondaryServerInfo serverInfo =ServerRegistry.getSecondaryServer(dnsMsg.getDomainName());
		if(serverInfo==null){
			System.out.println(this.getClass().getName() + " :Error : Failed to forward DNS message to secondary server");
			return;
		}
		DatagramPacket packet;
		byte []buff = new byte[Constants.DATA_BUFFER_SIZE];
		ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
		ObjectOutputStream objectOut=null;
		try{
			objectOut = new ObjectOutputStream(byteOut);
			objectOut.writeObject(dnsMsg);
			buff = byteOut.toByteArray();
			packet = new DatagramPacket(buff,buff.length,InetAddress.getByName(serverInfo.getIpAddress()),serverInfo.getPort());
			socket.send(packet);
			System.out.println(this.getClass().getName()+ ": Info: Message sent to Secondary : ");
			if(dnsMsg.getMsgType()==Constants.MSG_INQUIRY)
				System.out.println(" Type : Inquiry");
			else if(dnsMsg.getMsgType()==Constants.MSG_UPDATE)
				System.out.println(" Type : Update");
			else if(dnsMsg.getMsgType()==Constants.MSG_REGISTER)
				System.out.println(" Type : Register");
			else if(dnsMsg.getMsgType()==Constants.MSG_RESPONSE)
				System.out.println(" Type : Response");
			System.out.println(" Source : "+dnsMsg.getSource()+" \n Domain Nmae : "+dnsMsg.getDomainName()+"\n IP : "+dnsMsg.getIp_address());

			//System.out.println(this.getClass().getName()+ ": Info: Message sent to Secondary : \n Type : "+dnsMsg.getMsgType()+" \n Source : "+dnsMsg.getSource()+" \n Domain Nmae : "+dnsMsg.getDomainName()+"\n IP : "+dnsMsg.getIp_address());
		}
		 catch (IOException e) {
				System.out.println(this.getClass().getName()+": Error : Exception while creating the serializing or Seding the DNS_Message object to Secondary");
				e.printStackTrace();
			}
			finally{
				byteOut=null;
				objectOut=null;
				buff=null;
			}
	}
	
	public void sendToClient(){
		String clientIP = dnsMsg.getClientAddress();
		int port = dnsMsg.getClientPort();
		if(clientIP==null || port ==0){
			System.out.println(this.getClass().getName() + "Error : Failed to send DNS message to cleint, invalid address");
			return;
		}
		DatagramPacket sendPacket;
		byte []buff = new byte[Constants.DATA_BUFFER_SIZE];
		ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
		ObjectOutputStream objectOut;
		try {
			objectOut = new ObjectOutputStream(byteOut);
			objectOut.writeObject(dnsMsg);
			buff =byteOut.toByteArray();
			sendPacket = new DatagramPacket(buff, buff.length, InetAddress.getByName(clientIP), port);
			socket.send(sendPacket);
			System.out.println(this.getClass().getName()+ ": Info: Message sent to Client : ");
			if(dnsMsg.getMsgType()==Constants.MSG_INQUIRY)
				System.out.println(" Type : Inquiry");
			else if(dnsMsg.getMsgType()==Constants.MSG_UPDATE)
				System.out.println(" Type : Update");
			else if(dnsMsg.getMsgType()==Constants.MSG_REGISTER)
				System.out.println(" Type : Register");
			else if(dnsMsg.getMsgType()==Constants.MSG_RESPONSE)
				System.out.println(" Type : Response");
			System.out.println(" Source : "+dnsMsg.getSource()+" \n Domain Nmae : "+dnsMsg.getDomainName()+"\n IP : "+dnsMsg.getIp_address()+"\n Result : "+dnsMsg.getResult());

			//System.out.println(this.getClass().getName()+ ": Info: Message send to Client : \n Type : "+dnsMsg.getMsgType()+" \n Source : "+dnsMsg.getSource()+" \n Domain Nmae : "+dnsMsg.getDomainName()+"\n IP : "+dnsMsg.getIp_address()+"\n Result : "+dnsMsg.getResult());
		} catch (IOException e) {
			System.out.println(this.getClass().getName()+": Error : Exception while creating the serializing or sending the DNS message to client");
			e.printStackTrace();
		}
		finally{
			byteOut=null;
			objectOut=null;
			buff=null;
		}
		
	}
}
